﻿#region Copyright (c) 2015 Nathan Brown
/**********************************************************************************
*   Copyright (c) 2015 Nathan Brown
*   This work is licensed under the Microsoft Reciprocal License (MS-RL)
*   To view a copy of this license, visit http://www.microsoft.com/en-us/openness/licenses.aspx
*   Any use or rights not covered in above license are reserved.
*   Use of this work or any derivative constitute an acceptance of all license terms and conditions.
*   
*   Exact Synergy Enterprise(r) is a registered trademark of Exact International Development B.V. 
*   K2(r) is a registered trademark of SourceCode Technology Holdings, Inc.
*   
*   Project: NAB.K2.SynergyBroker
*   Namespace: NAB.K2.SynergyBroker
*   Written by: nathan.brown 
**********************************************************************************/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using SourceCode.SmartObjects.Services.ServiceSDK;
using SourceCode.SmartObjects.Services.ServiceSDK.Objects;
using SourceCode.SmartObjects.Services.ServiceSDK.Types;
using Metadata = NAB.K2.SynergyBroker.Exact.Synergy.Metadata;
using Entity = NAB.K2.SynergyBroker.Exact.Synergy.Entity;
using Entities = NAB.K2.SynergyBroker.Exact.Synergy.Entities;


namespace NAB.K2.SynergyBroker
{

    public class SynergyGenericBroker : ServiceAssemblyBase
    {

        private const string ENDPOINT_META = "/services/Exact.Metadata.svc";
        private const string ENDPOINT_ENTITY = "/services/Exact.Entity.svc";
        private const string ENDPOINT_ENTITIES = "/services/Exact.Entities.svc";

        private const string SVC_PROP_KEY = "KEY";
        private const string SVC_PROP_PROP_ISRESULT = "RESULT";

        private const string PROP_SAVE_RESULT = "EntitySaveResult";
        private const string PROP_SAVE_MESSAGE = "EntitySaveMessage";
        private const string PROP_LIST_SESSIONID = "#EntityListSessionId";

        private const string PARAM_LIST_QUERY_OPEARTION = "Query{0}Operation";
        private const string PARAM_LIST_QUERY_PROPERTY = "Query{0}Property";
        private const string PARAM_LIST_QUERY_VALUE = "Query{0}Value";

        private const string PARAM_LIST_SORT = "QuerySort";
        private const string PARAM_LIST_SESSION = "QuerySession";
        private const string PARAM_LIST_PAGESIZE = "QueryPageSize";

        private static bool HasInitialized = false;
        private static string[] ParamListQuerOp = null;
        private static string[] ParamListQuerProp = null;
        private static string[] ParamListQuerValue = null;

        //Current number of query parameters allowed
        private const int PARAM_QUERIES = 3;
       
        private static System.ServiceModel.Channels.Binding _binding = null;
        
        //private static System.ServiceModel.EndpointAddress _endpointMeta = null;
        //private static System.ServiceModel.EndpointAddress _endpointEntity = null;

        public SynergyGenericBroker()
        {
            _LoadParams();

        }


        private void _LoadParams()
        {

            if (!HasInitialized)
            {
                ParamListQuerOp = new string[PARAM_QUERIES];
                ParamListQuerProp = new string[PARAM_QUERIES];
                ParamListQuerValue = new string[PARAM_QUERIES];
                for (int i = 0; i < PARAM_QUERIES; i++)
                {
                    ParamListQuerOp[i] = string.Format(PARAM_LIST_QUERY_OPEARTION, (i + 1).ToString());
                    ParamListQuerProp[i] = string.Format(PARAM_LIST_QUERY_PROPERTY, (i + 1).ToString());
                    ParamListQuerValue[i] = string.Format(PARAM_LIST_QUERY_VALUE, (i + 1).ToString());

                    Console.WriteLine("Setting Op {0} to {1} ", i.ToString(), ParamListQuerOp[i]);
                    Console.WriteLine("Should be {0}", string.Format(PARAM_LIST_QUERY_OPEARTION, (i + 1).ToString()));
                }


                HasInitialized = true;
            }


        }

        private System.ServiceModel.Channels.Binding _GetBinding()
        {
            if(_binding == null)
            {
                _binding = new System.ServiceModel.BasicHttpBinding()
                {
                    Name = "serviceBinding",
                    MaxBufferSize = 10485760,
                    MaxReceivedMessageSize = 10485760,
                    Security = new System.ServiceModel.BasicHttpSecurity()
                    {
                        Mode = System.ServiceModel.BasicHttpSecurityMode.Transport,
                        Transport = new System.ServiceModel.HttpTransportSecurity() { ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows, ProxyCredentialType = System.ServiceModel.HttpProxyCredentialType.None },
                        Message = new System.ServiceModel.BasicHttpMessageSecurity() { ClientCredentialType=System.ServiceModel.BasicHttpMessageCredentialType.UserName, AlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Default}
                    }
                };

            }


            return _binding;
        }

        private void _SetSecurity<T>(System.ServiceModel.ClientBase<T> client) where T : class
        {

            AuthenticationMode m = this.Service.ServiceConfiguration.ServiceAuthentication.AuthenticationMode;
            switch (m)
            {
                case AuthenticationMode.Impersonate:
                    client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
                    break;

                case AuthenticationMode.Static:
                    string username = this.Service.ServiceConfiguration.ServiceAuthentication.UserName;
                    string password = this.Service.ServiceConfiguration.ServiceAuthentication.Password;

                    client.ClientCredentials.Windows.ClientCredential.UserName = username;
                    client.ClientCredentials.Windows.ClientCredential.Password = password;

                    Console.WriteLine("Setting Credentials {0}", username);

                    break;

                case AuthenticationMode.ServiceAccount:
                case AuthenticationMode.OAuth:
                case AuthenticationMode.SSO:
                    //All these will use the service account
                    client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;

                    break;
            }

        }



        public ServiceConfiguration SerivceConfiguration { get; set; }


        public override string GetConfigSection()
        {
            try
            {

                this.Service.ServiceConfiguration.Add("ServiceBaseURL", true, "https://<your synergy host>/<virtual directory>");
                
            }
            catch (Exception)
            {
                //Swallow exceptions
            }
            
            
            
            return base.GetConfigSection();
        }


        public override string DescribeSchema()
        {
            _LoadParams();


            TypeMappings map = new TypeMappings();

            map.Add("binary", SoType.File);
            map.Add("boolean", SoType.YesNo);
            map.Add("date", SoType.DateTime);
            map.Add("double", SoType.Decimal);
            map.Add("guid", SoType.Guid);
            map.Add("humanid", SoType.Text);
            map.Add("integer", SoType.Number);
            map.Add("number", SoType.Decimal);
            map.Add("picture", SoType.Image);
            map.Add("string", SoType.Text);
            map.Add("memo", SoType.Memo);

            this.Service.ServiceConfiguration.Add("Type Mappings", map);
            
            //Preform Discovery
            DiscoverSchemaHelper();

            //Setup Service Instance
            ServicePackage.IsSuccessful = true;

            return base.DescribeSchema();
        }




        private void DiscoverSchemaHelper()
        {
            string synUrl = this.Service.ServiceConfiguration["ServiceBaseURL"].ToString();

            Metadata.entitiesClient client = new Metadata.entitiesClient(_GetBinding(), new System.ServiceModel.EndpointAddress(synUrl + ENDPOINT_META));

            _SetSecurity<NAB.K2.SynergyBroker.Exact.Synergy.Metadata.entities>(client);
            //client.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
            

            //read the type mappings so that we can convert the DataTable's property types into
            //equivalent Service Object Property types
            TypeMappings map = (TypeMappings)this.Service.ServiceConfiguration["Type Mappings"];
            

            try
            {
                //Get all the entities
                var entityList = client.DefinedEntities();

                Console.WriteLine("Retrieved {0} entity definitions: ", entityList.Count.ToString());

                
                //iterate through each DataTable in the DataSource
                foreach (Metadata.DefinedEntity entityDef in entityList)
                {
                    Console.WriteLine("Retrieving {0}", entityDef.Name);



                    try
                    {

                        //1. CREATE SERVICE OBJECTS
                        ServiceObject svcObject = this.Service.ServiceObjects.Create(entityDef.Name);

                        //Set name and display
                        svcObject.MetaData.DisplayName = entityDef.Name;
                        svcObject.MetaData.Description = entityDef.Description;

                        //Get the actual metadata
                        Metadata.MetadataEntity entityMeta = client.Retrieve(entityDef.Name);

                        //Add the key proprety for future reference
                        svcObject.MetaData.ServiceProperties.Add(SVC_PROP_KEY, entityMeta.Interface.KeyProperty);

                        Console.WriteLine("Have Metadata {0}", entityMeta.Name);

                        Property svcProperty;

                        //2. CREATE SERVICE OBJECT PROPERTIES
                        foreach (var prop in entityMeta.Interface.Properties)
                        {
                            //note that the Name cannot have spaces
                            svcProperty = svcObject.Properties.Create(prop.Name);
                            svcProperty.Name = prop.Name;
                            svcProperty.MetaData.DisplayName = prop.Name;
                            svcProperty.MetaData.Description = prop.Description;
                            svcProperty.MetaData.ServiceProperties.Add(SVC_PROP_PROP_ISRESULT, false);

                            try
                            {

                                //We have to handle double and strings special because their types match actual types
                                switch (prop.Type)
                                {
                                    case Metadata.MetadataProperty.PropertyType.@double:
                                        svcProperty.SoType = map["double"];
                                        break;
                                    case Metadata.MetadataProperty.PropertyType.@string:
                                        if (prop.Length == 0)
                                        {
                                            svcProperty.SoType = map["memo"];
                                        }
                                        else
                                        {
                                            svcProperty.SoType = map["string"];
                                        }
                                        break;
                                    case Metadata.MetadataProperty.PropertyType.number:
                                        if(prop.Scale > 0)
                                        {
                                            svcProperty.SoType = map["double"];
                                        }else{
                                            svcProperty.SoType = map["integer"];
                                        }
                                        
                                        break;
                                    default:
                                        svcProperty.SoType = map[prop.Type.ToString()];
                                        break;
                                }

                            }
                            catch (Exception e)
                            {
                                Console.WriteLine("FAILED For prop {0} as {1}: {2}", prop.Name, prop.Type, e.Message);

                            }


                        }

                        //Add properties for Operation Results and Messages
                        svcProperty = svcObject.Properties.Create(PROP_SAVE_RESULT);
                        svcProperty.Name = PROP_SAVE_RESULT;
                        svcProperty.MetaData.DisplayName = "Entity Save Result";
                        svcProperty.MetaData.Description = "Indicates if the save complete successfully or not";
                        svcProperty.MetaData.ServiceProperties.Add(SVC_PROP_PROP_ISRESULT, true);
                        svcProperty.SoType = SoType.YesNo;

                        svcProperty = svcObject.Properties.Create(PROP_SAVE_MESSAGE);
                        svcProperty.Name = PROP_SAVE_MESSAGE;
                        svcProperty.MetaData.DisplayName = "Entity Save Message";
                        svcProperty.MetaData.Description = "Message regarding the save operation";
                        svcProperty.MetaData.ServiceProperties.Add(SVC_PROP_PROP_ISRESULT, true);
                        svcProperty.SoType = SoType.Memo;
                        
                        svcProperty = svcObject.Properties.Create(PROP_LIST_SESSIONID);
                        svcProperty.Name = PROP_LIST_SESSIONID;
                        svcProperty.MetaData.DisplayName = "Entity List Session Id";
                        svcProperty.MetaData.Description = "Session ID for a List Operation";
                        svcProperty.MetaData.ServiceProperties.Add(SVC_PROP_PROP_ISRESULT, true);
                        svcProperty.SoType = SoType.Number;
                        

                        Console.WriteLine("Properites {0} done", svcObject.Properties.Count.ToString());

                        
                        InputProperties inputProperties;
                        ReturnProperties returnProperties;


                        //Can only Read, Create, Delete if we have a Key
                        if (!string.IsNullOrEmpty(entityMeta.Interface.KeyProperty))
                        {
                            //////////////////////////////////////////////////////////////////////////
                            //READ Method
                            Method svcReadMethod = svcObject.Methods.Create("Read");
                            svcReadMethod.MetaData.DisplayName = "Read " + entityDef.Name;
                            svcReadMethod.Type = MethodType.Read;
                            
                            //Set up the input properties for the method
                            inputProperties = new InputProperties();

                            //for this method we will define the first column in the datatable as the input property
                            inputProperties.Add(svcObject.Properties[entityMeta.Interface.KeyProperty]);
                            svcReadMethod.InputProperties = inputProperties;
                            svcReadMethod.Validation.RequiredProperties.Add(svcObject.Properties[entityMeta.Interface.KeyProperty]);

                            //Set up the return properties for the Read Method
                            //for this method we'll return each column as a property.
                            returnProperties = new ReturnProperties();
                            foreach (Property p in svcObject.Properties)
                            {
                                if ((bool)p.MetaData.ServiceProperties[SVC_PROP_PROP_ISRESULT] == false)
                                {
                                    returnProperties.Add(p);
                                }
                            }
                            svcReadMethod.ReturnProperties = returnProperties;

                            //////////////////////////////////////////////////////////////////////////
                            //UPDATE Method
                            Method svcUpdateMethod = svcObject.Methods.Create("Update");
                            svcUpdateMethod.MetaData.DisplayName = "Update " + entityDef.Name;
                            svcUpdateMethod.Type = MethodType.Update;


                            //Set up the input properties for the method
                            inputProperties = new InputProperties();
                            foreach (Property p in svcObject.Properties)
                            {
                                //Only include if this is a return 
                                if ((bool)p.MetaData.ServiceProperties[SVC_PROP_PROP_ISRESULT] == false)
                                {
                                    inputProperties.Add(p);
                                }
                            }
                            svcUpdateMethod.InputProperties = inputProperties;
                            svcUpdateMethod.Validation.RequiredProperties.Add(svcObject.Properties[entityMeta.Interface.KeyProperty]);


                            //Set up the return properties for the Read Method
                            returnProperties = new ReturnProperties();
                            returnProperties.Add(svcObject.Properties[PROP_SAVE_RESULT]);
                            returnProperties.Add(svcObject.Properties[PROP_SAVE_MESSAGE]);
                            svcUpdateMethod.ReturnProperties = returnProperties;


                            //////////////////////////////////////////////////////////////////////////
                            //CREATE Method
                            Method svcCreateMethod = svcObject.Methods.Create("Create");
                            svcCreateMethod.MetaData.DisplayName = "Create " + entityDef.Name;
                            svcCreateMethod.Type = MethodType.Create;

                            //Set up the input properties for the method
                            inputProperties = new InputProperties();
                            foreach (Property p in svcObject.Properties)
                            {
                                //Only include if this is a return 
                                if ((bool)p.MetaData.ServiceProperties[SVC_PROP_PROP_ISRESULT] == false)
                                {
                                    inputProperties.Add(p);
                                }
                            }
                            svcCreateMethod.InputProperties = inputProperties;
                            
                            //Set all the manditory properties on create
                            foreach(var p in entityMeta.Interface.Properties.Where(r => r.Mandatory == true))
                            {
                                svcCreateMethod.Validation.RequiredProperties.Add(svcObject.Properties[p.Name]);
                            }

                            //Set up the return properties for the Read Method
                            returnProperties = new ReturnProperties();
                            returnProperties.Add(svcObject.Properties[entityMeta.Interface.KeyProperty]);
                            returnProperties.Add(svcObject.Properties[PROP_SAVE_RESULT]);
                            returnProperties.Add(svcObject.Properties[PROP_SAVE_MESSAGE]);
                            svcCreateMethod.ReturnProperties = returnProperties;


                            //////////////////////////////////////////////////////////////////////////
                            //DELETE
                            Method svcDeleteMethod = svcObject.Methods.Create("Delete");
                            svcDeleteMethod.MetaData.DisplayName = "Delete " + entityDef.Name;
                            svcDeleteMethod.Type = MethodType.Delete;


                            //Set up the input properties for the method
                            inputProperties = new InputProperties();
                            inputProperties.Add(svcObject.Properties[entityMeta.Interface.KeyProperty]);
                            svcDeleteMethod.InputProperties = inputProperties;
                            svcDeleteMethod.Validation.RequiredProperties.Add(svcObject.Properties[entityMeta.Interface.KeyProperty]);


                            //Set up the return properties for the Read Method
                            returnProperties = new ReturnProperties();
                            returnProperties.Add(svcObject.Properties[PROP_SAVE_RESULT]);
                            returnProperties.Add(svcObject.Properties[PROP_SAVE_MESSAGE]);
                            svcDeleteMethod.ReturnProperties = returnProperties;

                            //////////////////////////////////////////////////////////////////////////
                            //LIST Method
                            Method svcListMethod = svcObject.Methods.Create("List");
                            svcListMethod.MetaData.DisplayName = "List " + entityDef.Name;
                            svcListMethod.Type = MethodType.List;


                            //No input properties for list (all are parameters)
                            //inputProperties = new InputProperties();
                            //svcListMethod.InputProperties = inputProperties;

                            //Return all properties
                            returnProperties = new ReturnProperties();
                            foreach (Property p in svcObject.Properties)
                            {
                                if ((bool)p.MetaData.ServiceProperties[SVC_PROP_PROP_ISRESULT] == false)
                                {
                                    returnProperties.Add(p);
                                }
                            }
                            returnProperties.Add(svcObject.Properties[PROP_LIST_SESSIONID]);
                            svcListMethod.ReturnProperties = returnProperties;
                            
                            //Now all the query parameters
                            MethodParameter listMethodParameter;

                            for(int i = 0; i < PARAM_QUERIES; i++){
                                listMethodParameter = svcListMethod.MethodParameters.Create(ParamListQuerProp[i]);
                                listMethodParameter.MetaData.DisplayName = string.Format("Query {0} Property", i.ToString());
                                listMethodParameter.SoType = SoType.Text;
                                listMethodParameter.IsRequired = (i == 0);

                                listMethodParameter = svcListMethod.MethodParameters.Create(ParamListQuerOp[i]);
                                listMethodParameter.MetaData.DisplayName = string.Format("Query {0} Operation", i.ToString());
                                listMethodParameter.SoType = SoType.Text;
                                listMethodParameter.IsRequired = (i == 0);

                                listMethodParameter = svcListMethod.MethodParameters.Create(ParamListQuerValue[i]);
                                listMethodParameter.MetaData.DisplayName = string.Format("Query {0} Value", i.ToString());
                                listMethodParameter.SoType = SoType.Text;
                                listMethodParameter.IsRequired = (i == 0);
                            }

                            listMethodParameter = svcListMethod.MethodParameters.Create(PARAM_LIST_SORT);
                            listMethodParameter.MetaData.DisplayName = "Sorting Expression";
                            listMethodParameter.SoType = SoType.Text;
                            listMethodParameter.IsRequired = false;
                                                        
                            listMethodParameter = svcListMethod.MethodParameters.Create(PARAM_LIST_SESSION);
                            listMethodParameter.MetaData.DisplayName = "Session Id";
                            listMethodParameter.SoType = SoType.Number;
                            listMethodParameter.IsRequired = true;
                            
                            listMethodParameter = svcListMethod.MethodParameters.Create(PARAM_LIST_PAGESIZE);
                            listMethodParameter.MetaData.DisplayName = "Page Size";
                            listMethodParameter.SoType = SoType.Number;
                            listMethodParameter.IsRequired = true;            
                            

                        }

                        

                        //Activate the Service Abject
                        svcObject.Active = true;

                    }catch(Exception e)
                    {
                        Console.WriteLine("Exception in Entity {0}: {1}", entityDef.Name, e.Message);

                    }
                }
        
            }catch(Exception e){
                Console.WriteLine("Exception in Metadata: " + e.Message);
            }

        }


        public override void Execute()
        {
            //Ensure parameters are loaded
            _LoadParams();
            
            //Get the URL as configuration
            string synUrl = this.Service.ServiceConfiguration["ServiceBaseURL"].ToString();
            

            //2. GET THE SERVICE OBJECT THAT WAS REQUESTED
            //at runtime, the requested Service Object is always in the [0] position of the Service.ServiceObjects array
            ServiceObject serviceObject = Service.ServiceObjects[0];

            //3. GET THE METHOD THAT WAS REQUESTED
            //at runtime, the requested Service Object Method is always in the [0] position of the ServiceObject.Methods array
            Method method = serviceObject.Methods[0];

            //4. GET THE INPUT PROPERTIES AND RETURN PROPERTIES FOR THE REQUESTED METHOD
            // InputProperties and ReturnProperties are string collections, create property
            //collections for later ease-of-use.
            Property[] inputs = new Property[method.InputProperties.Count];
            Property[] returns = new Property[method.ReturnProperties.Count];

            try
            {
                //populate the Input Properties collection
                for (int i = 0; i < method.InputProperties.Count; i++)
                {
                    inputs[i] = serviceObject.Properties[method.InputProperties[i]];
                }

                //populate the return properties collection
                for (int i = 0; i < method.ReturnProperties.Count; i++)
                {
                    returns[i] = serviceObject.Properties[method.ReturnProperties[i]];
                }

            }
            catch (Exception e)
            {
                Console.WriteLine("Exception setting up execute: {0}", e.Message);
                throw;
            }
            
            
            try
            {
                if (method.Type == MethodType.Create || method.Type == MethodType.Delete || method.Type == MethodType.Read || method.Type == MethodType.Update)
                {

                    //Create the client
                    Entity.entityClient client = new Entity.entityClient(_GetBinding(), new System.ServiceModel.EndpointAddress(synUrl + ENDPOINT_ENTITY));
                    _SetSecurity<NAB.K2.SynergyBroker.Exact.Synergy.Entity.entity>(client);

                    ExecuteMethodCRUD(client, inputs, returns, method.Type, serviceObject);
                }
                else if (method.Type == MethodType.List)
                {
                    //Create the client
                    Entities.entitysetClient client = new Entities.entitysetClient(_GetBinding(), new System.ServiceModel.EndpointAddress(synUrl + ENDPOINT_ENTITIES));
                    _SetSecurity<NAB.K2.SynergyBroker.Exact.Synergy.Entities.entityset>(client);

                    ExecuteMethodList(client, inputs, returns, method, serviceObject);

                }

            }
            catch (Exception e)
            {
                Console.WriteLine("Exception calling {0} on {1}: {2}", method.Name, serviceObject.Name, e.Message);
                throw;
            }

            
            // Indicate that the operation was successful.
            ServicePackage.IsSuccessful = true;

        }

        private object _GetMethodParam(Method m, string paramName, object defaultValue)
        {
            MethodParameter p = m.MethodParameters[paramName];
            if(p == null)
            {
                return defaultValue;
            }
            else
            {
                return p.Value ?? defaultValue;
            }
        }


        private void ExecuteMethodList(Entities.entitysetClient client, Property[] inputs, Property[] returns, Method method, ServiceObject serviceObject)
        {
            //Create the entity
            Entities.RetrieveCriteria rc = new Entities.RetrieveCriteria();
            rc.EntityName = serviceObject.Name;

            rc.BatchSize = Convert.ToInt32(_GetMethodParam(method, PARAM_LIST_PAGESIZE, 10));
            rc.SessionId = Convert.ToInt64(_GetMethodParam(method, PARAM_LIST_SESSION, 0));


            //Add Properties to Query and Query to ReceiveCriteria
            Entities.FilterQuery query = new Entities.FilterQuery();
            rc.FilterQuery = query;

            List<Entities.QueryProperty> props = new List<Entities.QueryProperty>();
            query.Properties = props;
            

            Entities.QueryProperty prop;
            int index;
            object paramValue;
            for (int i = 0; i < PARAM_QUERIES; i++)
            {
                index = method.MethodParameters.IndexOf(ParamListQuerOp[i]);
                if (index >= 0)
                {
                    if (!method.MethodParameters[index].IsClear)
                    {
                        paramValue = method.MethodParameters[index].Value;

                        if (paramValue != null)
                        {
                            prop = new Entities.QueryProperty();
                            prop.Operation = method.MethodParameters[index].Value.ToString();
                            prop.PropertyName = _GetMethodParam(method, ParamListQuerProp[i], string.Empty).ToString();
                            prop.PropertyValue = _GetMethodParam(method, ParamListQuerValue[i], string.Empty);
                                                    
                            props.Add(prop);
                        }
                        
                    }
                }
            }

            //Check for sort
            index = method.MethodParameters.IndexOf(PARAM_LIST_SORT);
            if (!method.MethodParameters[index].IsClear && method.MethodParameters[index].Value != null)
            {
                prop = new Entities.QueryProperty();
                prop.Operation = "=";
                prop.PropertyName = "#SORT";
                prop.PropertyValue = method.MethodParameters[index].Value;

                if (!string.IsNullOrEmpty(prop.PropertyValue.ToString()))
                {
                    props.Add(prop);
                }
            }

            
            Entities.EntitiesData data = client.RetrieveSet(rc);

            //Initialize a result table
            serviceObject.Properties.InitResultTable();
            foreach (var entity in data.Entities)
            {
                foreach (var p in entity.Properties)
                {
                    serviceObject.Properties[p.Name].Value = p.Value;
                }
                serviceObject.Properties[PROP_LIST_SESSIONID].Value = data.SessionID;

                serviceObject.Properties.BindPropertiesToResultTable();
            }

            //Try and return the session
            method.MethodParameters[PARAM_LIST_SESSION].Value = data.SessionID;
            
        }


        private void ExecuteMethodCRUD(Entity.entityClient client, Property[] inputs, Property[] returns, MethodType
            methodType, ServiceObject serviceObject) {

            string keyField = serviceObject.MetaData.ServiceProperties[SVC_PROP_KEY].ToString();


            //Create the entity
            Entity.EntityData entity = new Entity.EntityData();
            entity.EntityName = serviceObject.Name;
            
            //READ
            if(methodType == MethodType.Read)
            {
                List<Entity.PropertyData> i = new List<Entity.PropertyData>();
                foreach(var p in inputs){
                    i.Add(new Entity.PropertyData() { Name = p.Name, Value = p.Value});
                }

                //Set the properties
                entity.Properties = i;

                //Call retrieve
                entity = client.Retrieve(entity);

                if(entity == null)
                {
                    Console.WriteLine("Entity call returned NULL");
                }

                //Initialize a result table
                serviceObject.Properties.InitResultTable();
                foreach(var p in entity.Properties)
                {
                    serviceObject.Properties[p.Name].Value = p.Value;
                }
                serviceObject.Properties.BindPropertiesToResultTable();
                

            }
            else if (methodType == MethodType.Update)
            {
                //First we read
                List<Entity.PropertyData> i = new List<Entity.PropertyData>();
                foreach (var p in inputs)
                {
                    if (!p.IsClear && p.Name == keyField)
                    {
                        i.Add(new Entity.PropertyData() { Name = p.Name, Value = p.Value });
                    }
                }
                entity.Properties = i;

                bool success = false;
                string message = string.Empty;

                try
                {
                    entity = client.Retrieve(entity);
                                        
                    //Then set all
                    i = new List<Entity.PropertyData>();
                    foreach (var p in inputs)
                    {
                        if (!p.IsClear && p.Value != null)
                        {
                            i.Add(new Entity.PropertyData() { Name = p.Name, Value = p.Value });
                            //Console.WriteLine("Adding save param {0} = {1}", p.Name, p.Value);
                        }
                    }
                    entity.Properties = i;
                    
                    //Validate
                    //client.Validate(entity);
                    
                    //Save
                    client.Update(entity);
                    success = true;

                }
                catch (System.ServiceModel.FaultException f)
                {
                    message = _faultToMessage(f);
                }
                catch (Exception e)
                {
                    message = _exceptionToMessage(e);
                }

               
                //Initialize a result table
                serviceObject.Properties.InitResultTable();
                serviceObject.Properties[PROP_SAVE_RESULT].Value = success;
                serviceObject.Properties[PROP_SAVE_MESSAGE].Value = message;
                serviceObject.Properties.BindPropertiesToResultTable();

            }
            else if (methodType == MethodType.Create)
            {
                //Input the fields
                List<Entity.PropertyData> i = new List<Entity.PropertyData>();
                foreach (var p in inputs)
                {
                    if (!p.IsClear)
                    {
                        i.Add(new Entity.PropertyData() { Name = p.Name, Value = p.Value });
                    }
                }
                entity.Properties = i;

                bool success = false;
                string message = string.Empty;

                try
                {
                    //Delete
                    entity = client.Create(entity);
                    success = true;

                    //Find the key field
                    foreach(var p in entity.Properties)
                    {
                        if(p.Name == keyField)
                        {
                            //Set and break;
                            serviceObject.Properties[keyField].Value = p.Value;
                            break;
                        }
                    }

                }
                catch (System.ServiceModel.FaultException f)
                {
                    message = _faultToMessage(f);
                }
                

                //Initialize a result table
                serviceObject.Properties.InitResultTable();
                serviceObject.Properties[PROP_SAVE_RESULT].Value = success;
                serviceObject.Properties[PROP_SAVE_MESSAGE].Value = message;
                serviceObject.Properties.BindPropertiesToResultTable();

            }
            else if (methodType == MethodType.Delete)
            {
                //Input the fields
                List<Entity.PropertyData> i = new List<Entity.PropertyData>();
                foreach (var p in inputs)
                {
                    if (!p.IsClear)
                    {
                        i.Add(new Entity.PropertyData() { Name = p.Name, Value = p.Value });
                    }
                }
                entity.Properties = i;

                bool success = false;
                string message = string.Empty;

                try
                {
                    //Delete
                    client.Delete(entity);
                    success = true;
                }
                catch (System.ServiceModel.FaultException f)
                {
                    message = _faultToMessage(f);
                }
                catch (Exception e)
                {
                    message = _exceptionToMessage(e);
                }


                //Initialize a result table
                serviceObject.Properties.InitResultTable();
                serviceObject.Properties[PROP_SAVE_RESULT].Value = success;
                serviceObject.Properties[PROP_SAVE_MESSAGE].Value = message;
                serviceObject.Properties.BindPropertiesToResultTable();

            }

            
        }

        private string _faultToMessage(System.ServiceModel.FaultException e)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(e.Message);

            System.ServiceModel.FaultException<Entity.EntityFault> fe = e as System.ServiceModel.FaultException<Entity.EntityFault>;
            if (fe != null)
            {
                Entity.EntityFault ef = fe.Detail;

                foreach (Entity.WSExceptionInfo ei in ef.Exceptions)
                {
                    sb.Append(ei.PropertyName);
                    sb.Append(": ");
                    sb.Append(ei.Message);
                    sb.Append("/r/n");

                }
            }

            
            
            return sb.ToString();

        }

        private string _exceptionToMessage(Exception e)
        {
            StringBuilder sb = new StringBuilder();

           
            while (e != null)
            {
                sb.Append(e.Message);

                if (e.InnerException != null)
                {
                    sb.Append("-> ");
                    e = e.InnerException;
                }
                else
                {
                    e = null;
                }
                

            }

            return sb.ToString();

        }



        public override void Extend()
        {
            


        }
    }
}
